﻿using AnyLink.Domain.Const;
using AnyLink.Domain.Dtos;
using AnyLink.Domain.Options;
using AnyLink.Domain.ViewModels;
using AnyLink.Infrastructure;
using AnyLink.Infrastructure.Auth;
using AnyLink.Infrastructure.Cache;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using SqlSugar;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace AnyLink.WebApi.Controllers
{
    /// <summary>
    /// 鉴权相关
    /// </summary>
    [ApiController]
    [Route("auth")]
    public class AuthController : BaseController
    {
        readonly ICache _cache; 
        readonly JwtOptions _jwtOptions;

        public AuthController(ICache cache,JwtOptions jwtOptions) {
            _cache = cache;
            _jwtOptions = jwtOptions;
        }
        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("login")]
        [ProducesResponseType(typeof(LoginUserView), StatusCodes.Status200OK)]
        public async Task<IActionResult> LoginAsync(LoginDto dto)
        {
            try
            {
                #region 校验用户信息
                var userid = "123";
                var username = "admin";
                #endregion

                #region 签发JWT

                //用户信息
                var claims = new[] {
                    new Claim(ClaimAttributes.UserId, userid),
                    new Claim(ClaimAttributes.UserName, username)
                };

                return Ok(CreateRefreshToken(claims));
                #endregion
            }
            catch (Exception ex)
            {
                return Error($"登录异常:{ex.Message}");
            }
        }

        /// <summary>
        /// 刷新访问令牌
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("refresh")]
        [ProducesResponseType(typeof(LoginUserView), StatusCodes.Status200OK)]
        public async Task<IActionResult> RefreshAsync(RefreshTokenDto dto)
        {
            try
            {
                var jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(dto.AccessToken);
                var userid = jwtToken.Claims.FirstOrDefault(a => a.Type == ClaimAttributes.UserId)?.Value;
                
                var refreshtoken = _cache.Get<string>($"{CacheConst.RefreshToken}{userid}");
                if (refreshtoken == null) return Error("未找到该刷新令牌");
                if (refreshtoken != dto.RefreshToken) return Error("刷新令牌不正确");
                #region 签发JWT                
                return Ok(CreateRefreshToken(jwtToken.Claims));
                #endregion
            }
            catch (Exception)
            {
                return Error("访问令牌解析失败");
            }
        }

        /// <summary>
        /// 创建刷新令牌
        /// </summary>
        /// <param name="claims"></param>
        /// <returns></returns>
        [NonAction]
        private LoginUserView CreateRefreshToken(IEnumerable<Claim> claims)
        {
            var userid = claims.FirstOrDefault(a => a.Type == ClaimAttributes.UserId)?.Value;
            var username = claims.FirstOrDefault(a => a.Type == ClaimAttributes.UserName)?.Value;
            #region 签发JWT
            //生成一个新的刷新令牌
            var refreshtoken = Guid.NewGuid().ToString();
            _cache.Set($"{CacheConst.RefreshToken}{userid}", refreshtoken, TimeSpan.FromDays(30));
            var view = new LoginUserView
            {
                Expires = DateTime.Now.AddMinutes(_jwtOptions.ExpireMinutes),
                RefreshToken = refreshtoken
            };
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.SecurityKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken(
                issuer: _jwtOptions.Issuer,
                audience: _jwtOptions.Audience,
                claims: claims,
                expires: view.Expires,
                signingCredentials: creds);
            view.AccessToken = new JwtSecurityTokenHandler().WriteToken(token);
            return view;
            #endregion
        }
    }
}
